/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package UDP;

import BittorentPackage.BittorentPackageMessage;
import BittorentPackage.ClientChunks;
import FolderChunks.FilenameChunk;
import FolderChunks.InfoChunk;
import FolderChunks.InfoChunkFile;
import common.function_common;
import common.global;
import hashChunk.SplitFile;
import java.awt.Desktop;
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import managerPeer.Node;

/**
 *
 * @author DongVan
 */
public class Listener extends Thread {

    private int listenerPort = 59;
    private DatagramSocket listenerSocket = null;
    private int MAX_PACKET_SIZE = 1000;
    private JFrame layoutMain = null;
    private FilenameChunk fChunks = null;
    private FilenameChunk fChunksReceived = null;
    private int numOfResponseTimes = 0;
    private String destinationStoreFileDownload = "";
    private boolean checkFolderContainFileExists = false;
    private ArrayList<ClientChunks> listClientSendChunksFile = null;
    private ArrayList<ClientChunks> listClientRequestedSendFileToMe = null;
    // Default Constructor

    public Listener() {
        super();
        listenerPort = global.port;
        MAX_PACKET_SIZE = global.MAX_PACKET_SIZE;
    }

    // Alternate Constructor to Override Port
    public Listener(int newPort, FilenameChunk param_fChunks, JFrame param_main_layout, String param_destination_store_file_downloaded) {
        // overide port setting
        this.listenerPort = newPort;
        this.fChunks = new FilenameChunk(param_fChunks);
        this.fChunksReceived = new FilenameChunk(param_fChunks);
        this.listClientSendChunksFile = new ArrayList<>();
        this.layoutMain = param_main_layout;
        this.destinationStoreFileDownload = param_destination_store_file_downloaded;
    }

    @Override
    public void run() {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        try {
            DatagramSocket socket = new DatagramSocket(listenerPort);
            socket.setSoTimeout(0);
            do {
                //Nhan thong diep tu Client
                byte[] rcvbuf = new byte[1024];
                //Tao DatagramPacket de nhan data
                DatagramPacket rcvpkt = new DatagramPacket(rcvbuf, rcvbuf.length);
                InetAddress fromAddress = rcvpkt.getAddress();
                int port = rcvpkt.getPort();
                //Goi ham nhan DatagramPacket
                socket.receive(rcvpkt);
                BittorentPackageMessage pkReceived = function_common.deserializeBytes(rcvpkt.getData());
                switch (pkReceived.getM_Option()) {
                    case "2":
                        // listenner other client send chunks which they keep . 
                        if (this.numOfResponseTimes == global.maxClientInLanIgnoreMe) {
                            processingListChunksResponseAndRequestDownloadChunks();
                        } else {
                            FilenameChunk fChunksReceived = (FilenameChunk) pkReceived.getM_Data();
                            this.listClientSendChunksFile.add(new ClientChunks(fChunksReceived, port, fromAddress.getHostAddress()));
                        }
                        break;
                    case "3": // receive request to get chunks send to client need to downloading.
                        FilenameChunk fnameChunk = (FilenameChunk) pkReceived.getM_Data();
                        if (fnameChunk != null) {
                            for (InfoChunk info : fnameChunk.getM_ListChunk()) {
                                File fileFromDownload = new File(global.folderDownloadPath + "\\" + info.getFileNameChunk());
                                if (fileFromDownload.exists() && fileFromDownload.canRead()) {
                                    InfoChunkFile infoChunkFile = new InfoChunkFile(info, function_common.fileToArrayByte(fileFromDownload));
                                    BittorentPackageMessage eachPackageSendChunks = new BittorentPackageMessage("4", infoChunkFile);
                                    Sender udpSender = new Sender();
                                    udpSender.sendData(port, fromAddress.getHostAddress(), eachPackageSendChunks);
                                    continue;
                                }
                                File fileFromShared = new File(global.folderSharedPath + "\\" + info.getFileNameChunk());
                                if (fileFromShared.exists() && fileFromShared.canRead()) {
                                    InfoChunkFile infoChunkFile = new InfoChunkFile(info, function_common.fileToArrayByte(fileFromShared));
                                    BittorentPackageMessage eachPackageSendChunks = new BittorentPackageMessage("4", infoChunkFile);
                                    Sender udpSender = new Sender();
                                    udpSender.sendData(port, fromAddress.getHostAddress(), eachPackageSendChunks);
                                }
                            }
                        }
                        break;
                    case "4":
                        if (!checkFolderContainFileExists) {
                            boolean resultCheckAndCreate = function_common.checkFolderContainChunksIsExists(global.folderDownloadPath, fChunksReceived.getM_Filename());
                            if (!resultCheckAndCreate) {
                                continue;
                            }
                            checkFolderContainFileExists = true;
                        }
                        InfoChunkFile infoChunkFileReceived = (InfoChunkFile) pkReceived.getM_Data();
                        String path = global.folderDownloadPath + "\\" + fChunksReceived.getFolderNameContainChunks();
                        if (fChunksReceived.getM_ListChunk().contains(infoChunkFileReceived.getInfoChunk())) {
                            fChunksReceived.getM_ListChunk().remove(infoChunkFileReceived.getInfoChunk());
                            String filenameChunk = infoChunkFileReceived.getInfoChunk().getFileNameChunk();
                            function_common.ReceiveFile(path, filenameChunk, infoChunkFileReceived.getFileToByte());
                        }
                        if (fChunksReceived.isEmpty()) {
                            // call function merge
                            SplitFile spFile = new SplitFile();
                            File fileMerge = spFile.mergeFiles(path, this.destinationStoreFileDownload, fChunksReceived.getM_Filename());
                            if (fileMerge != null) {
                                int reply = JOptionPane.showConfirmDialog(this.layoutMain,
                                        "File is downloaded successfully", "Do you want to open location file ??? ", JOptionPane.YES_NO_OPTION);
                                if (reply == JOptionPane.NO_OPTION) {
                                    this.interrupt();
                                } else if(reply == JOptionPane.YES_OPTION) {
                                    String pathOnDesktop = fileMerge.getAbsolutePath();
                                    File dir = new File(pathOnDesktop.substring(0, pathOnDesktop.lastIndexOf(File.separator)));
                                    if (Desktop.isDesktopSupported()) {
                                        Desktop.getDesktop().open(dir);
                                    }
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            } while (!global.stopListenerUDP);
            //dong ket noi
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean checkChunkIsExistsInList(InfoChunk info) {
        if (!this.listClientRequestedSendFileToMe.isEmpty()) {
            for (ClientChunks client : this.listClientRequestedSendFileToMe) {
                if (client.getM_Chunk().getM_ListChunk().contains(info)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void processingListChunksResponseAndRequestDownloadChunks() {
        if (this.listClientSendChunksFile == null || this.listClientSendChunksFile.isEmpty()) {
            return;
        }
        Collections.sort(this.listClientSendChunksFile);
        boolean flagProcessingRequestGetChunksOfFileFromOtherClient = false;
        for (ClientChunks client : this.listClientSendChunksFile) {
            ArrayList<InfoChunk> chunksInfo = new ArrayList<>();
            for (InfoChunk info : client.getM_Chunk().getM_ListChunk()) {
                if (this.checkChunkIsExistsInList(info)) {
                    chunksInfo.add(info);
                    this.fChunks.removeItem(info);
                    if (this.fChunks.isEmpty()) {
                        flagProcessingRequestGetChunksOfFileFromOtherClient = true;
                        break;
                    }
                }
            }
            if (!chunksInfo.isEmpty()) {
                this.listClientRequestedSendFileToMe.add(
                        new ClientChunks(
                        new FilenameChunk(client.getM_Chunk().getM_Filename(), chunksInfo),
                        client.getM_Port(), client.getM_ClientIP()));
            }
            if (!flagProcessingRequestGetChunksOfFileFromOtherClient) {
                RequestGetChunksOfFileFromOtherClient();
                break;
            }
        }
    }

    public void RequestGetChunksOfFileFromOtherClient() {
        if (this.listClientRequestedSendFileToMe.isEmpty()) {
            return;
        }
        for (ClientChunks client : this.listClientRequestedSendFileToMe) {
            BittorentPackageMessage pkSend = new BittorentPackageMessage("3", client.getM_Chunk());
            Sender udpSender = new Sender();
            udpSender.sendData(client.getM_Port(), client.getM_ClientIP(), pkSend);
        }

    }
}
